package mini.util;

import java.util.HashMap;

/**
 * This is a convenience class for getting symbolic references to constants,
 * adding them to the symbol table if needed. For example, if someone has to add
 * the numeric value of '1' to something, they have to reference a memory
 * location where that value is stored. So there will be a method to return the
 * symbolic name of such a memory location. It'll also get added to the symbol
 * table (if not there already) to ensure it gets allocated in the memory table.
 * 
 * @author Jennifer
 * 
 */
public class SymbolicNames {

	// By starting the names with 'Constant', the symbol table will
	// realize they need to be located in the global memory area,
	// event though they aren't really constants. It's hackish.
	private static final String SP = "ConstantSP";
	private static final String FP = "ConstantFP";
	private static final String FPB ="ConstantFPB";
	private static final String SCRATCH ="ConstantScratch";
	
	// Locations to store these in memory
	private static final int SP_LOC = 0;	// CAL instruction in RAM machine assumes SP is at memory loc 0
	private static final int FP_LOC = 1;
	private static final int FPB_LOC = 2;
	private static final int SCRATCH_LOC = 3;
	private static final int NEXT_MEMORY_LOC = 4;
	
	// What we prefer temps, constants, user variables, locations, parameters, and procedures with
	private static final String TEMP_VARIABLE_PREFIX = "temp";
	private static final String CONSTANT_PREFIX = "Constant";
	private static final String LOCATION_PREFIX = "loc";
	private static final String PROCEDURE_PREFIX = "Procedure";
	private static final String PARAMETER_PREFIX = "param-";
	private static final String USER_VARIABLE_PREFIX = "user-";
	
	// TODO: Probably should add prefixes to parametes and user variables to avoid name collisions
	// For example, if someone names their own user variable 'temp', it will get interpreted to
	// be a temp variable

	/**
	 * Gets symbolic name for an integer constant
	 * @return Symbolic name for an integer constant; if not
	 * already in the hm (symbol table), will add it so it
	 * gets initialized
	 */
	public static String getSymbolicNameForInteger( HashMap<String,Integer> hm, Integer value) {
		
		// We know what its name is, by definition
		String symbol = generateConstantLabel(value);
		
		// But does it already exist, defined?
		boolean defined = false;
		for( String symbolName : hm.keySet() ) {
			if( symbolName.equals(symbol) ) {
				defined = true;
			}
		}
		
		// If it's not there, add it. Convention for naming is to use the
		// constant prefix and append the numeric value
		if( !defined ) {
			hm.put(symbol, value);
		}
		
		// And return it
		return symbol;
	}
	
	/**
	 * Gets symbolic name for the stack pointer.
	 * @return Symbolic name for SP
	 */
	public static String getSymbolicNameForSP() {
		return SP;
	}

	/**
	 * Gets symbolic name for the frame pointer buffer.
	 * @return Symbolic name for FPB
	 */
	public static String getSymbolicNameForFPB() {
		return FPB;
	}

	/**
	 * Gets symbolic name for the scratch.
	 * @return Symbolic name for Scratch
	 */
	public static String getSymbolicNameForScratch() {
		return SCRATCH;
	}

	/**
	 * Gets symbolic name for the frame pointer.
	 * @return Symbolic name for FP
	 */
	public static String getSymbolicNameForFP() {
		return FP;
	}
	
	/**
	 * Gets the memory location where the frame pointer goes.
	 * @return A numeric memory location
	 */
	public static int getMemoryLocForFP() {
		return FP_LOC;
	}
	
	/**
	 * Gets the memory location where the stack pointer goes.
	 * @return A numeric memory location
	 */
	public static int getMemoryLocForSP() {
		return SP_LOC;
	}
	
	/**
	 * Gets the memory location where the frame pointer buffer goes.
	 * @return A numeric memory location
	 */
	public static int getMemoryLocForFPB() {
		return FPB_LOC;
	}

	/**
	 * Gets the memory location where the scratch goes.
	 * @return A numeric memory location
	 */
	public static int getMemoryLocForScratch() {
		return SCRATCH_LOC;
	}

	/**
	 * Returns the first numeric memory address after the
	 * space reserved for the FP, SP, FPB, and SCRATCH
	 * 
	 * @return First unused memory address after he buffers
	 */
	public static int getMemoryLocAfterPointers() {
		return NEXT_MEMORY_LOC;
	}
	
	/**
	 * Generates a unique label suitable for use as a temp variable
	 * reference. The counter is what is used to make it unique.
	 * 
	 * @return Unique label suitable for use as a temp variable label
	 */
	public static String generateTempVariableLabel( int counter ) {
		return TEMP_VARIABLE_PREFIX + counter;
	}
	
	/**
	 * Generates a unique label suitable for use as a user variable
	 * reference. The variable name is what is used to make it unique.
	 * 
	 * @return Unique label suitable for use as a user variable label
	 */
	public static String generateUserVariableLabel( String variable ) {
		return USER_VARIABLE_PREFIX + variable;
	}
	
	/**
	 * Generates a unique label suitable for use as a constant
	 * reference. The counter is what is used to make it unique.
	 * 
	 * @return Unique label suitable for use as a constant reference
	 */
	public static String generateConstantLabel( int counter ) {
		return CONSTANT_PREFIX + counter;
	}
	
	/**
	 * Generates a unique label suitable for use as a location
	 * label. The counter is what is used to make it unique.
	 * 
	 * @return Unique label suitable for use as a location label
	 */
	public static String generateLocationLabel( int counter ) {
		return LOCATION_PREFIX + counter;
	}
	
	/**
	 * Generates a unique label suitable for use as a parameter
	 * label. The counter is what is used to make it unique.
	 * 
	 * @return Unique label suitable for use as a parameter label
	 */
	public static String generateParameterLabel( String param ) {
		return PARAMETER_PREFIX + param;
	}

	/**
	 * Generates a unique label suitable for use as a procedure
	 * label. The counter is what is used to make it unique.
	 * 
	 * @return Unique label suitable for use as a procedure label
	 */
	public static String generateProcedureLabel( int counter ) {
		return PROCEDURE_PREFIX + counter;
	}

    /**
     * What identifies a constantlabel is that it starts with the Constant prefix
     * and has no spaces in it
     * @param variableName Variable name
     * @return True if constant, false otherwise
     */
    public static boolean isConstant(String variableName ) {
    	return variableName.startsWith(CONSTANT_PREFIX) && !variableName.trim().contains(" ");
    }

    /**
     * What identifies a location label is that it starts with the Location prefix
     * and has no spaces in it
     * @param variableName Variable name
     * @return True if location, false otherwise
     */
    public static boolean isLocationLabel(String variableName ) {
    	return variableName.startsWith(LOCATION_PREFIX) && !variableName.trim().contains(" ");
    }

    /**
     * What identifies a temp variable label is that it starts with the TempVar prefix
     * and has no spaces in it
     * @param variableName Variable name
     * @return True if temp variable, false otherwise
     */
    public static boolean isTempVariable(String variableName ) {
    	return variableName.startsWith(TEMP_VARIABLE_PREFIX) && !variableName.trim().contains(" ");
    }

    /**
     * What identifies a user variable label is that it starts with the UserVar prefix
     * and has no spaces in it
     * @param variableName Variable name
     * @return True if user variable, false otherwise
     */
    public static boolean isUserVariable(String variableName ) {
    	return variableName.startsWith(USER_VARIABLE_PREFIX) && !variableName.trim().contains(" ");
    }

    /**
     * What identifies a procedure label is that it starts with the Procedure prefix
     * and has no spaces in it
     * @param line The trimmed line
     * @return True if it's a procedure label, false otherwise
     */
	public static boolean isProcedureLabel(String line) {
		return line.startsWith( PROCEDURE_PREFIX ) && !line.trim().contains(" ");
	}
	
    /**
     * What identifies a parameter label is that it starts with the Parameter prefix
     * and has no spaces in it
     * @param line The trimmed line
     * @return True if it's a parameter label, false otherwise
     */
	public static boolean isParameterLabel(String line) {
		return line.startsWith( PARAMETER_PREFIX ) && !line.trim().contains(" ");
	}
	
}
